﻿using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using NewLife.Log;

namespace NewLife.Agent
{
    /// <summary>Linux版进程守护</summary>
    public class Systemd : Host
    {
        private readonly String _path;
        //private ServiceBase _service;

        /// <summary>用于执行服务的用户</summary>
        public String User { get; set; }

        /// <summary>用于执行服务的用户组</summary>
        public String Group { get; set; }

        /// <summary>实例化</summary>
        public Systemd()
        {
            var ps = new[] {
                "/etc/systemd/system",
                "/run/systemd/system",
                "/usr/lib/systemd/system",
                "/lib/systemd/system" };
            foreach (var p in ps)
            {
                if (Directory.Exists(p))
                {
                    _path = p;
                    break;
                }
            }
        }

        /// <summary>启动服务</summary>
        /// <param name="service"></param>
        public override void Run(ServiceBase service)
        {
            if (service == null) throw new ArgumentNullException(nameof(service));

            // 以服务运行
            InService = true;

            try
            {
                // 启动初始化
                service.StartLoop();

                // 阻塞
                service.DoLoop();

                // 停止
                service.StopLoop();
            }
            catch (Exception ex)
            {
                XTrace.WriteException(ex);
            }
        }

        /// <summary>服务是否已安装</summary>
        /// <param name="serviceName">服务名</param>
        /// <returns></returns>
        public override Boolean IsInstalled(String serviceName)
        {
            var file = _path.CombinePath($"{serviceName}.service");

            return File.Exists(file);
        }

        /// <summary>服务是否已启动</summary>
        /// <param name="serviceName">服务名</param>
        /// <returns></returns>
        public override Boolean IsRunning(String serviceName)
        {
            var file = _path.CombinePath($"{serviceName}.service");
            if (!File.Exists(file)) return false;

            var str = Execute("systemctl", $"status {serviceName}", false);
            if (!str.IsNullOrEmpty() && str.Contains("running")) return true;

            return false;
        }

        /// <summary>安装服务</summary>
        /// <param name="serviceName">服务名</param>
        /// <param name="displayName">显示名</param>
        /// <param name="binPath">文件路径</param>
        /// <param name="description">描述信息</param>
        /// <returns></returns>
        public override Boolean Install(String serviceName, String displayName, String binPath, String description)
        {
            if (User.IsNullOrEmpty())
            {
                // 从命令行参数加载用户设置 -user
                var args = Environment.GetCommandLineArgs();
                if (args.Length >= 1)
                {
                    for (var i = 0; i < args.Length; i++)
                    {
                        if (args[i].EqualIgnoreCase("-user") && i + 1 < args.Length)
                        {
                            User = args[i + 1];
                            break;
                        }
                        if (args[i].EqualIgnoreCase("-group") && i + 1 < args.Length)
                        {
                            Group = args[i + 1];
                            break;
                        }
                    }
                    if (!User.IsNullOrEmpty() && Group.IsNullOrEmpty()) Group = User;
                }
            }

            return Install(_path, serviceName, displayName, binPath, description, User, Group);
        }

        /// <summary>安装服务</summary>
        /// <param name="systemdPath">systemd目录有</param>
        /// <param name="serviceName">服务名</param>
        /// <param name="displayName">显示名</param>
        /// <param name="binPath">文件路径</param>
        /// <param name="description">描述信息</param>
        /// <param name="user">用户</param>
        /// <param name="group">用户组</param>
        /// <returns></returns>
        public static Boolean Install(String systemdPath, String serviceName, String displayName, String binPath, String description, String user, String group)
        {
            XTrace.WriteLine("{0}.Install {1}, {2}, {3}, {4}", typeof(Systemd).Name, serviceName, displayName, binPath, description);

            var file = systemdPath.CombinePath($"{serviceName}.service");
            XTrace.WriteLine(file);

            //var asm = Assembly.GetEntryAssembly();
            var des = !displayName.IsNullOrEmpty() ? displayName : description;

            var sb = new StringBuilder();
            sb.AppendLine("[Unit]");
            sb.AppendLine($"Description={des}");

            sb.AppendLine();
            sb.AppendLine("[Service]");
            sb.AppendLine("Type=simple");
            //sb.AppendLine($"ExecStart=/usr/bin/dotnet {asm.Location}");
            sb.AppendLine($"ExecStart={binPath}");
            sb.AppendLine($"WorkingDirectory={".".GetFullPath()}");
            if (!user.IsNullOrEmpty()) sb.AppendLine($"User={user}");
            if (!group.IsNullOrEmpty()) sb.AppendLine($"Group={group}");
            sb.AppendLine("Restart=on-failure");

            sb.AppendLine();
            sb.AppendLine("[Install]");
            sb.AppendLine("WantedBy=multi-user.target");

            File.WriteAllText(file, sb.ToString());

            // sudo systemctl daemon-reload
            // sudo systemctl enable StarAgent
            // sudo systemctl start StarAgent

            Process.Start("systemctl", "daemon-reload");
            Process.Start("systemctl", $"enable {serviceName}");
            //Execute("systemctl", $"start {serviceName}");

            return true;
        }

        /// <summary>卸载服务</summary>
        /// <param name="serviceName">服务名</param>
        /// <returns></returns>
        public override Boolean Remove(String serviceName)
        {
            XTrace.WriteLine("{0}.Remove {1}", GetType().Name, serviceName);

            var file = _path.CombinePath($"{serviceName}.service");
            if (File.Exists(file)) File.Delete(file);

            return true;
        }

        /// <summary>启动服务</summary>
        /// <param name="serviceName">服务名</param>
        /// <returns></returns>
        public override Boolean Start(String serviceName)
        {
            XTrace.WriteLine("{0}.Start {1}", GetType().Name, serviceName);

            return Process.Start("systemctl", $"start {serviceName}") != null;
        }

        /// <summary>停止服务</summary>
        /// <param name="serviceName">服务名</param>
        /// <returns></returns>
        public override Boolean Stop(String serviceName)
        {
            XTrace.WriteLine("{0}.Stop {1}", GetType().Name, serviceName);

            return Process.Start("systemctl", $"stop {serviceName}") != null;
        }

        /// <summary>重启服务</summary>
        /// <param name="serviceName">服务名</param>
        public override Boolean Restart(String serviceName)
        {
            XTrace.WriteLine("{0}.Restart {1}", GetType().Name, serviceName);

            if (InService)
                return Process.Start("systemctl", $"restart {serviceName}") != null;
            else
                return Process.Start(Service.GetExeName(), "-run -delay") != null;
        }

        private static String Execute(String cmd, String arguments, Boolean writeLog = true)
        {
            if (writeLog) XTrace.WriteLine("{0} {1}", cmd, arguments);

            var psi = new ProcessStartInfo(cmd, arguments)
            {
                UseShellExecute = false,
                RedirectStandardOutput = true
            };
            var process = Process.Start(psi);
            if (!process.WaitForExit(3_000))
            {
                process.Kill();
                return null;
            }

            return process.StandardOutput.ReadToEnd();
        }
    }
}